package main

import (
	// --- 标准库 ---
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	// --- Web 框架 ---
	"github.com/gin-gonic/gin"

	// --- 数据库相关 ---
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"

	"github.com/redis/go-redis/extra/redisotel/v9" // 正确的 Redis OTel 包
	// --- Redis 客户端 ---
	"github.com/redis/go-redis/v9"

	// --- 其他第三方库 ---
	"github.com/google/uuid"

	// --- OpenTelemetry 核心 API ---
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/codes"
	"go.opentelemetry.io/otel/trace"

	// --- OpenTelemetry SDK ---
	"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
	"go.opentelemetry.io/otel/propagation"
	"go.opentelemetry.io/otel/sdk/resource" // 导入 resource 包
	sdktrace "go.opentelemetry.io/otel/sdk/trace"

	// --- OpenTelemetry 语义约定 ---
	semconv "go.opentelemetry.io/otel/semconv/v1.24.0" // 导入以使用常量

	"github.com/uptrace/opentelemetry-go-extra/otelgorm"
	// --- OpenTelemetry 自动插桩 (Contrib & Extra) ---
	"go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin"
)

// --- 全局变量 ---
var (
	tracer trace.Tracer
	db     *gorm.DB
	rdb    *redis.Client
)

// --- GORM 数据模型 ---
type User struct {
	ID        string `gorm:"type:varchar(100);primaryKey"`
	Name      string `gorm:"type:varchar(100);not null"`
	Email     string `gorm:"type:varchar(100);unique;not null"`
	CreatedAt time.Time
	UpdatedAt time.Time
}

// --- OpenTelemetry 初始化 ---

// initTracerProvider 初始化 OpenTelemetry SDK TracerProvider。
func initTracerProvider() (func(context.Context) error, error) {
	log.Println("初始化 OTel Tracer Provider...")

	// 1. 创建 Exporter (stdout)
	exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
	if err != nil {
		return nil, fmt.Errorf("创建 stdout exporter 失败: %w", err)
	}
	log.Println("OTel Exporter (stdout) 创建成功。")

	// 2. 创建 Resource (服务信息)
	// **关键修正:** 合并 Default 资源和只包含自定义属性的 Schemaless 资源
	// Default() 提供基础属性和 SDK 的 SchemaURL。
	// NewSchemaless() 创建只包含传入属性的资源，本身不带 SchemaURL。
	// Merge() 会保留 Default() 的 SchemaURL 并合并所有属性。
	res, err := resource.Merge(
		resource.Default(), // 提供基础属性和 SDK 默认 Schema URL
		resource.NewSchemaless( // 提供自定义属性，这些函数都返回 attribute.KeyValue
			semconv.ServiceName("gin-gorm-mysql-redis-service"), // 服务名
			semconv.ServiceVersion("v1.4.3"),                    // 服务版本 (更新)
			attribute.String("environment", "development"),      // 环境
		),
	)
	if err != nil {
		// 如果合并失败 (理论上不应再失败)
		return nil, fmt.Errorf("创建 resource 失败: %w", err)
	}
	log.Println("OTel Resource 创建成功，服务名:", "gin-gorm-mysql-redis-service")

	// 3. 创建 TracerProvider
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
		sdktrace.WithBatcher(exporter),
		sdktrace.WithResource(res), // 使用合并后的 Resource
	)
	log.Println("OTel TracerProvider 配置完成。")

	// 4. 设置全局 Provider
	otel.SetTracerProvider(tp)

	// 5. 设置全局 Propagator
	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
		propagation.TraceContext{},
		propagation.Baggage{},
	))
	log.Println("OTel 全局 Provider 和 Propagator 设置完成。")

	return tp.Shutdown, nil // 返回关闭函数
}

// --- 数据库和 Redis 初始化 ---

// initDB 初始化 GORM 并连接到 MySQL 数据库。
func initDB() (*gorm.DB, error) {
	dsn := os.Getenv("MYSQL_DSN")
	if dsn == "" {
		dsn = "root:123456@tcp(127.0.0.1:3306)/otel_demo?charset=utf8mb4&parseTime=True&loc=Local&multiStatements=true"
		log.Println("警告: MYSQL_DSN 环境变量未设置，使用默认 DSN")
	}

	log.Println("正在连接到 MySQL 数据库 (DSN:", dsn, ")")
	gormConfig := &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	}
	database, err := gorm.Open(mysql.Open(dsn), gormConfig)

	// --- 尝试自动创建数据库 ---
	if err != nil {
		log.Printf("连接数据库失败 (可能数据库 'otel_demo' 不存在)，尝试自动创建: %v", err)
		rootDSN := os.Getenv("MYSQL_ROOT_DSN")
		if rootDSN == "" {
			rootDSN = "root:123456@tcp(127.0.0.1:3306)/?charset=utf8mb4&parseTime=True&loc=Local&multiStatements=true"
			log.Println("警告: MYSQL_ROOT_DSN 环境变量未设置，使用默认 root DSN")
		}
		rootDB, rootErr := gorm.Open(mysql.Open(rootDSN), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)})
		if rootErr == nil {
			log.Println("成功连接到 MySQL 服务器 (无特定数据库)。正在尝试创建数据库 'otel_demo'...")
			createSQL := "CREATE DATABASE IF NOT EXISTS `otel_demo` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
			createErr := rootDB.Exec(createSQL).Error
			sqlDB, _ := rootDB.DB()
			if sqlDB != nil {
				sqlDB.Close()
			} // 关闭临时连接
			if createErr != nil {
				return nil, fmt.Errorf("自动创建数据库 'otel_demo' 失败: %w", createErr)
			}
			log.Println("数据库 'otel_demo' 创建成功或已存在。重新连接...")
			database, err = gorm.Open(mysql.Open(dsn), gormConfig) // 重新连接
			if err != nil {
				return nil, fmt.Errorf("数据库创建后重新连接失败: %w", err)
			}
		} else {
			log.Printf("连接 root DSN (%s) 失败: %v", rootDSN, rootErr)
			return nil, fmt.Errorf("连接数据库失败 (DSN: %s)，且无法通过 root DSN (%s) 连接 MySQL 服务器来尝试创建数据库: %w", dsn, rootDSN, err)
		}
	}
	log.Println("成功连接到 MySQL 数据库 'otel_demo'。")

	// --- 集成 OTel GORM 插件 ---
	log.Println("正在集成 OTel GORM 插件...")
	if err := database.Use(otelgorm.NewPlugin(
		otelgorm.WithAttributes(semconv.DBSystemMySQL),
		otelgorm.WithDBName("otel_demo"),
	)); err != nil {
		log.Printf("警告: 集成 otelgorm 插件失败: %v。数据库操作将不会被自动追踪。", err)
	} else {
		log.Println("OTel GORM 插件集成成功。")
	}

	// --- 数据库迁移 ---
	log.Println("正在执行数据库自动迁移 (AutoMigrate)...")
	err = database.AutoMigrate(&User{})
	if err != nil {
		return nil, fmt.Errorf("数据库迁移 (AutoMigrate) 失败: %w", err)
	}
	log.Println("数据库迁移完成。")

	return database, nil
}

// initRedis 初始化 Redis 客户端连接并集成 OTel (redisotel)。
func initRedis() (*redis.Client, error) {
	redisAddr := os.Getenv("REDIS_ADDR")
	if redisAddr == "" {
		redisAddr = "127.0.0.1:6379"
		log.Println("警告: REDIS_ADDR 环境变量未设置，使用默认地址 127.0.0.1:6379")
	}
	redisPassword := os.Getenv("REDIS_PASSWORD")

	log.Printf("正在连接到 Redis (地址: %s)...", redisAddr)
	rdbClient := redis.NewClient(&redis.Options{
		Addr:     redisAddr,
		Password: redisPassword,
		DB:       0,
	})

	// --- 集成 OTel Redis 插件 (redisotel) ---
	log.Println("正在集成 OTel Redis 插件 (redisotel/v9)...")
	if err := redisotel.InstrumentTracing(rdbClient); err != nil { // 使用 redisotel
		log.Printf("警告: 集成 redisotel 插件失败: %v。Redis 操作将不会被自动追踪。", err)
	} else {
		log.Println("OTel Redis 插件 (redisotel) 集成成功。")
	}

	// --- 测试连接 ---
	log.Println("正在测试 Redis 连接 (PING)...")
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	_, err := rdbClient.Ping(ctx).Result()
	if err != nil {
		return nil, fmt.Errorf("连接 Redis (地址: %s) 失败 (PING 失败): %w", redisAddr, err)
	}
	log.Println("成功连接到 Redis 并收到 PONG。")

	return rdbClient, nil
}

// --- Gin HTTP Handler 和业务逻辑 ---

// getUserHandler 处理 GET /users/:id 请求
func getUserHandler(c *gin.Context) {
	ctx := c.Request.Context()
	userID := c.Param("id")
	if userID == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "用户 ID 不能为空"})
		return
	}

	var handlerSpan trace.Span
	ctx, handlerSpan = tracer.Start(ctx, "Handler: GetUserByID",
		trace.WithAttributes(
			attribute.String("app.user.id", userID),
			semconv.HTTPRequestMethodKey.String(c.Request.Method),
			semconv.URLPathKey.String(c.FullPath()),
		),
		trace.WithSpanKind(trace.SpanKindInternal),
	)
	defer handlerSpan.End()

	handlerSpan.AddEvent("开始处理 GetUser 请求")
	user, err := findUserByID(ctx, userID) // 调用业务逻辑

	if err != nil {
		handlerSpan.RecordError(err, trace.WithAttributes(attribute.String("app.error.type", "UserLookupFailed")))
		if errors.Is(err, gorm.ErrRecordNotFound) {
			handlerSpan.SetStatus(codes.Error, "User not found")
			c.JSON(http.StatusNotFound, gin.H{"error": "用户未找到"})
		} else {
			handlerSpan.SetStatus(codes.Error, "Internal error finding user")
			c.JSON(http.StatusInternalServerError, gin.H{"error": "查找用户时发生内部错误: " + err.Error()})
		}
		return
	}

	handlerSpan.SetStatus(codes.Ok, "User found")
	handlerSpan.AddEvent("成功获取用户数据", trace.WithAttributes(attribute.String("app.user.name", user.Name)))
	handlerSpan.SetAttributes(attribute.String("app.user.retrieved_id", user.ID))
	c.JSON(http.StatusOK, user)
}

// createUserHandler 处理 POST /users 请求
func createUserHandler(c *gin.Context) {
	ctx := c.Request.Context()
	var newUser User
	if err := c.ShouldBindJSON(&newUser); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求数据: " + err.Error()})
		return
	}

	var handlerSpan trace.Span
	ctx, handlerSpan = tracer.Start(ctx, "Handler: CreateUser",
		trace.WithAttributes(
			attribute.Bool("app.user.email_provided", newUser.Email != ""), // 使用 Bool
			semconv.HTTPRequestMethodKey.String(c.Request.Method),
			semconv.URLPathKey.String(c.FullPath()),
		),
		trace.WithSpanKind(trace.SpanKindInternal),
	)
	defer handlerSpan.End()

	handlerSpan.AddEvent("开始处理 CreateUser 请求")
	if newUser.ID == "" {
		newUser.ID = uuid.NewString()
		handlerSpan.SetAttributes(
			attribute.String("app.user.id_generated", "true"),
			attribute.String("app.user.id", newUser.ID),
		)
		log.Printf("为新用户生成 ID: %s", newUser.ID)
	} else {
		handlerSpan.SetAttributes(
			attribute.String("app.user.id_generated", "false"),
			attribute.String("app.user.id", newUser.ID),
		)
	}

	if newUser.Name == "" || newUser.Email == "" {
		err := errors.New("用户名和邮箱不能为空")
		handlerSpan.RecordError(err)
		handlerSpan.SetStatus(codes.Error, "Invalid input data")
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	createdUser, err := saveUser(ctx, &newUser) // 调用业务逻辑

	if err != nil {
		handlerSpan.RecordError(err, trace.WithAttributes(attribute.String("app.error.type", "UserCreationFailed")))
		handlerSpan.SetStatus(codes.Error, "Failed to create user")
		c.JSON(http.StatusInternalServerError, gin.H{"error": "创建用户失败: " + err.Error()})
		return
	}

	handlerSpan.SetStatus(codes.Ok, "User created")
	handlerSpan.SetAttributes(attribute.String("app.user.created_id", createdUser.ID))
	handlerSpan.AddEvent("用户创建成功")
	c.JSON(http.StatusCreated, createdUser)
}

// --- 业务逻辑层 (Service Layer) ---

// findUserByID 业务逻辑：根据 ID 查找用户（缓存优先）。
func findUserByID(ctx context.Context, id string) (*User, error) {
	var bizSpan trace.Span
	ctx, bizSpan = tracer.Start(ctx, "Service: FindUserByID",
		trace.WithAttributes(attribute.String("app.user.id", id)),
		trace.WithSpanKind(trace.SpanKindInternal),
	)
	defer bizSpan.End()

	var user User
	var cacheHit bool = false

	cacheKey := fmt.Sprintf("user:%s", id)
	bizSpan.AddEvent("检查 Redis 缓存", trace.WithAttributes(attribute.String("cache.key", cacheKey)))
	jsonData, err := rdb.Get(ctx, cacheKey).Result() // OTel (redisotel) 自动追踪

	if err == nil {
		cacheHit = true
		bizSpan.AddEvent("缓存命中")
		bizSpan.SetAttributes(attribute.Bool("cache.hit", true))
		if errUnmarshal := json.Unmarshal([]byte(jsonData), &user); errUnmarshal != nil {
			bizSpan.RecordError(errUnmarshal, trace.WithAttributes(attribute.String("app.error.type", "CacheUnmarshalFailed")))
			bizSpan.SetStatus(codes.Error, "缓存数据反序列化失败")
			log.Printf("错误: 反序列化用户 '%s' 的缓存数据失败: %v", id, errUnmarshal)
			bizSpan.AddEvent("缓存反序列化失败，继续查询数据库")
			cacheHit = false
		} else {
			bizSpan.SetStatus(codes.Ok, "用户在缓存中找到")
			return &user, nil
		}
	} else if !errors.Is(err, redis.Nil) {
		bizSpan.RecordError(err, trace.WithAttributes(attribute.String("app.error.type", "RedisError")))
		bizSpan.SetStatus(codes.Error, "Redis 缓存检查失败")
		log.Printf("警告: 检查用户 '%s' 的缓存时发生 Redis 错误: %v", id, err)
		bizSpan.AddEvent("Redis Get 操作出错，继续查询数据库")
	} else {
		bizSpan.AddEvent("缓存未命中")
		bizSpan.SetAttributes(attribute.Bool("cache.hit", false))
	}

	if !cacheHit {
		bizSpan.AddEvent("查询数据库")
		result := db.WithContext(ctx).First(&user, "id = ?", id) // OTel (otelgorm) 自动追踪
		if result.Error != nil {
			bizSpan.RecordError(result.Error)
			if errors.Is(result.Error, gorm.ErrRecordNotFound) {
				bizSpan.SetAttributes(attribute.Bool("db.found", false))
				bizSpan.SetStatus(codes.Error, "用户在数据库中未找到")
			} else {
				bizSpan.SetAttributes(attribute.String("app.error.type", "DatabaseError"))
				bizSpan.SetStatus(codes.Error, "数据库查询失败")
			}
			return nil, result.Error
		}
		bizSpan.SetAttributes(attribute.Bool("db.found", true))
		bizSpan.AddEvent("数据库查询成功")

		bizSpan.AddEvent("更新 Redis 缓存")
		jsonDataBytes, errMarshal := json.Marshal(&user)
		if errMarshal != nil {
			bizSpan.RecordError(errMarshal, trace.WithAttributes(attribute.String("app.error.type", "CacheMarshalFailed")))
			bizSpan.AddEvent("警告: 用户数据序列化失败，无法更新缓存")
			log.Printf("警告: 序列化用户 '%s' 数据以便写入缓存失败: %v", id, errMarshal)
			bizSpan.SetStatus(codes.Ok, "用户在数据库中找到，但缓存更新失败 (序列化错误)")
			return &user, nil
		}
		cacheTTL := time.Duration(5+rand.Intn(6)) * time.Minute
		bizSpan.AddEvent("设置缓存", trace.WithAttributes(
			attribute.String("cache.key", cacheKey),
			attribute.String("cache.ttl", cacheTTL.String()),
		))
		setCmd := rdb.Set(ctx, cacheKey, string(jsonDataBytes), cacheTTL) // OTel (redisotel) 自动追踪
		if errSet := setCmd.Err(); errSet != nil {
			bizSpan.RecordError(errSet, trace.WithAttributes(attribute.String("app.error.type", "RedisError")))
			bizSpan.AddEvent("警告: Redis 缓存更新失败")
			log.Printf("警告: 更新用户 '%s' 的 Redis 缓存失败: %v", id, errSet)
			bizSpan.SetStatus(codes.Ok, "用户在数据库中找到，但缓存更新失败 (Redis Set 错误)")
			return &user, nil
		}
		bizSpan.AddEvent("缓存更新成功")
		bizSpan.SetAttributes(attribute.Bool("cache.updated", true))
		bizSpan.SetStatus(codes.Ok, "用户在数据库中找到并已更新缓存")
		return &user, nil
	}

	log.Println("警告: findUserByID 执行流程异常结束")
	bizSpan.SetStatus(codes.Error, "内部逻辑错误")
	return nil, errors.New("findUserByID 内部逻辑错误")
}

// saveUser 业务逻辑：保存新用户到数据库，并使相关缓存失效。
func saveUser(ctx context.Context, user *User) (*User, error) {
	var bizSpan trace.Span
	ctx, bizSpan = tracer.Start(ctx, "Service: SaveUser",
		trace.WithAttributes(
			attribute.String("app.user.id", user.ID),
			attribute.String("app.user.email", user.Email), // 注意脱敏
		),
		trace.WithSpanKind(trace.SpanKindInternal),
	)
	defer bizSpan.End()

	var dbErrorOccurred bool = false
	var cacheErrorOccurred bool = false
	var dbErr, cacheErr error

	bizSpan.AddEvent("开始保存用户到数据库")
	result := db.WithContext(ctx).Create(user) // OTel (otelgorm) 自动追踪
	if result.Error != nil {
		dbErrorOccurred = true
		dbErr = result.Error
		bizSpan.RecordError(dbErr, trace.WithAttributes(attribute.String("app.error.type", "DatabaseError")))
		bizSpan.SetStatus(codes.Error, "保存用户到数据库失败")
		log.Printf("错误: 保存用户 (ID: %s) 到数据库失败: %v", user.ID, dbErr)
		return nil, fmt.Errorf("数据库创建用户失败: %w", dbErr)
	}
	bizSpan.SetAttributes(attribute.Int64("db.rows_affected", result.RowsAffected))
	bizSpan.AddEvent("用户成功保存到数据库")

	bizSpan.AddEvent("使 Redis 缓存失效")
	cacheKey := fmt.Sprintf("user:%s", user.ID)
	bizSpan.SetAttributes(attribute.String("cache.key", cacheKey))
	delCmd := rdb.Del(ctx, cacheKey) // OTel (redisotel) 自动追踪
	deletedCount := delCmd.Val()
	bizSpan.SetAttributes(attribute.Int64("cache.invalidated_count", deletedCount))
	if err := delCmd.Err(); err != nil && !errors.Is(err, redis.Nil) { // 忽略 key 不存在错误
		cacheErrorOccurred = true
		cacheErr = err
		bizSpan.RecordError(cacheErr, trace.WithAttributes(attribute.String("app.error.type", "RedisError")))
		bizSpan.AddEvent("警告: Redis 缓存失效失败")
		log.Printf("警告: 失效用户 '%s' 的 Redis 缓存失败: %v", user.ID, cacheErr)
	} else {
		if deletedCount > 0 {
			bizSpan.AddEvent("缓存成功失效")
		} else {
			bizSpan.AddEvent("缓存键不存在，无需失效")
		}
	}

	if dbErrorOccurred { /* 错误已设置 */
	} else if cacheErrorOccurred {
		bizSpan.SetStatus(codes.Ok, "用户已保存到数据库，但缓存失效失败")
	} else {
		bizSpan.SetStatus(codes.Ok, "用户成功保存，缓存已处理")
	}

	return user, nil
}

// --- 程序主入口 ---
func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	//log.SetFlags(log.LstdFlags | log.Llongfile)
	log.Println("应用程序启动中...")

	// --- 1. 初始化 OpenTelemetry ---
	log.Println("正在初始化 OpenTelemetry...")
	shutdown, err := initTracerProvider() // 调用修正后的 initTracerProvider
	if err != nil {
		log.Fatalf("致命错误: 初始化 TracerProvider 失败: %v", err)
	}
	defer func() { /* OTel 关闭 */
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()
		log.Println("正在关闭 OpenTelemetry TracerProvider...")
		if err := shutdown(ctx); err != nil {
			log.Printf("错误: 关闭 TracerProvider 时发生错误: %v", err)
		} else {
			log.Println("OpenTelemetry TracerProvider 已成功关闭。")
		}
	}()
	tracer = otel.Tracer("gin-gorm-mysql-redis-app/main")
	log.Println("OpenTelemetry 初始化完成。")

	// --- 2. 初始化数据库 ---
	log.Println("正在初始化数据库连接...")
	db, err = initDB()
	if err != nil {
		log.Fatalf("致命错误: 初始化数据库失败: %v", err)
	}
	sqlDB, err := db.DB()
	if err == nil {
		log.Println("正在配置数据库连接池...")
		sqlDB.SetMaxIdleConns(10)
		sqlDB.SetMaxOpenConns(100)
		sqlDB.SetConnMaxLifetime(time.Hour)
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		defer cancel()
		if err := sqlDB.PingContext(ctx); err != nil {
			log.Fatalf("致命错误: 数据库 Ping 失败: %v", err)
		}
		log.Println("数据库连接池配置完成并 Ping 成功。")
	} else {
		log.Printf("警告: 获取 *sql.DB 对象失败，无法配置连接池: %v", err)
	}
	log.Println("数据库初始化完成。")

	// --- 3. 初始化 Redis ---
	log.Println("正在初始化 Redis 连接...")
	rdb, err = initRedis()
	if err != nil {
		log.Fatalf("致命错误: 初始化 Redis 失败: %v", err)
	}
	defer func() { /* Redis 关闭 */
		log.Println("正在关闭 Redis 连接...")
		if err := rdb.Close(); err != nil {
			log.Printf("错误: 关闭 Redis 连接时发生错误: %v", err)
		} else {
			log.Println("Redis 连接已成功关闭。")
		}
	}()
	log.Println("Redis 初始化完成。")

	// --- 4. 初始化 Gin 引擎 ---
	log.Println("正在初始化 Gin Web 引擎...")
	router := gin.New()
	router.Use(otelgin.Middleware("gin-server"))
	router.Use(gin.Logger())
	router.Use(gin.Recovery())
	log.Println("Gin 引擎初始化完成，OTel 和基础中间件已添加。")

	// --- 5. 定义 API 路由 ---
	log.Println("正在定义 API 路由...")
	userRoutes := router.Group("/users")
	{
		userRoutes.POST("", createUserHandler)
		userRoutes.GET("/:id", getUserHandler)
	}
	router.GET("/health", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"status": "UP"})
	})
	log.Println("API 路由定义完成。")

	// --- 6. 配置并启动 HTTP 服务器 ---
	log.Println("正在配置 HTTP 服务器...")
	server := &http.Server{
		Addr:         ":8080",
		Handler:      router,
		ReadTimeout:  15 * time.Second,
		WriteTimeout: 15 * time.Second,
		IdleTimeout:  60 * time.Second,
	}

	go func() { /* 异步启动 */
		log.Printf("HTTP 服务器正在启动，监听地址 %s", server.Addr)
		randSuffix := rand.Intn(10000)
		log.Printf("服务名 (OTel): gin-gorm-mysql-redis-service")
		log.Printf("HTTP 服务名 (otelgin): gin-server")
		log.Printf("数据库: MySQL @ (配置的 DSN)")
		log.Printf("Redis: @ (配置的地址)")
		log.Printf("OTel Exporter: stdout")
		log.Printf("-----------------------------------")
		log.Printf("访问 GET /health 检查服务状态")
		log.Printf("访问 POST /users 创建用户 (示例):\n  curl -X POST http://localhost:8080/users -H 'Content-Type: application/json' -d '{\"name\":\"Test User %d\",\"email\":\"test%d@example.com\"}'", randSuffix, randSuffix)
		log.Printf("访问 GET /users/{id} 获取用户 (使用上面创建的 ID)")
		log.Printf("-----------------------------------")
		if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
			log.Fatalf("致命错误: HTTP 服务器启动失败: %v", err)
		}
		log.Println("HTTP 服务器监听协程已正常退出。")
	}()

	// --- 7. 处理优雅停机 ---
	log.Println("设置优雅停机信号监听 (SIGINT, SIGTERM)...")
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
	sig := <-quit
	log.Printf("接收到信号 %q，开始执行优雅停机...", sig)
	ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
	defer cancel()
	log.Println("正在优雅地关闭 HTTP 服务器...")
	if err := server.Shutdown(ctx); err != nil {
		log.Printf("错误: HTTP 服务器优雅停机失败: %v", err)
	} else {
		log.Println("HTTP 服务器已优雅关闭。")
	}
	log.Println("应用程序即将退出。")
}
